#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../csock/csock.h"
#include "chttp.h"

// +-------------------------------------------------------
// | 内部函数 
// +-------------------------------------------------------

/** 复制字符串的一部分 **/
static char *substr(const char *s, size_t i, size_t len)
{
    char *d = (char *)malloc(len + 1);
    memcpy(d, s, len);
    d[len] = '\0';
    return d;
}

/** 是否为IPv4 **/
static int is_ipv4(const char *ip_address)
{
    int a = 0, b = 0, c = 0, d = 0;
    int re = 0;
    
    if (!ip_address)
        return 0;
    
    re = sscanf(ip_address, "%d.%d.%d.%d", &a, &b, &c, &d);
    return (re == 4) 
        && (a >= 1 && a < 255) && (b >= 0 && b < 255) 
        && (c >= 0 && c < 255) && (d >= 1 && d < 255);
}

/** 忽略大小写strstr **/
static const char* stristr(const char *s1, const char *s2)
{
    char *p = NULL, *s1b = NULL, *s2b = NULL;

    if (!s1 || !s2) return NULL;

    s1b = strdup(s1);
    s2b = strdup(s2);
    if (!s1b || !s2b) {
        if (s1b) free(s1b);
        if (s2b) free(s2b);
        return NULL;
    }

    p = s1b;
    while (*p != '\0') {
        *p = tolower(*p);
        p++;
    }

    p = s2b;
    while (*p != '\0') {
        *p = tolower(*p);
        p++;
    }

    p = strstr(s1b, s2b);
    free(s1b);
    free(s2b);
    return p == NULL ? NULL : s1 + (p - s1b);
}

/** 获得文件长度 **/
static long readfilesize (FILE *fp) {
    long size = 0;
    if (!fp) return 0;
    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    return size;
}

static const char *ALPHA_BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static int base64_encode (const char *s, char *d, size_t n) {
    int i = 0, j = 0, l = 0, lbuf = 0;
    char buf0 = '\0', buf1 = '\0', buf2 = '\0';
    
    if (!s || !d || n == 0) return 0;
    
    l = strlen(s);
    lbuf = l % 3;
    for (i = 0, j = 0; i < l && j < (int)n; ) {
        buf0 = s[i++];
        buf1 = (i < l) ? s[i++] : 0;
        buf2 = (i < l) ? s[i++] : 0;
        d[j++] = ALPHA_BASE64[(buf0 >> 2) & 0x3F]; if (j == (int)n) break;
        d[j++] = ALPHA_BASE64[((buf0 << 4) | ((buf1 & 0xFF) >> 4)) & 0x3F]; if (j == (int)n) break;
        d[j++] = ALPHA_BASE64[((buf1 << 2) | ((buf2 & 0xFF) >> 6)) & 0x3F]; if (j == (int)n) break;
        d[j++] = ALPHA_BASE64[buf2 & 0x3F];
    }
    
    switch (lbuf)
    {
    case 1:
        d[--j] = '=';
    case 2:
        d[--j] = '=';
    }

    return j;
}

static char *_http_base64_encode(const char *buf) {
    char *d = (char *)calloc(1, strlen(buf) * 2 + 1);
    if (!d) return d;
    base64_encode(buf, d, strlen(buf) * 2);
    return d;
}

// +-------------------------------------------------------
// | http/https - net 
// +-------------------------------------------------------

typedef struct cHTTPFormNet {
    int s;
#ifdef USE_OPENSSL
    SSLSocket *ss;
#endif
} cHTTPFormNet;

struct cHTTPForm {
    const char *url;

    // GET/POST
    cHTTPFormMethod method;

    // 超时时间
    int timeout;

    // HTTP代理服务器
    cHTTPFormNetHTTPProxy *proxy;

    // URL信息
    const char *protocal;
    char *doname;
    char *path;
    int port;

    // 网络
    cHTTPFormNet *net;

    // 回调
    void *p, *p2;
    void (*cb)(void *p, unsigned char *data, size_t size);
    void (*cb2)(void *p, const char *temp);

    // 301/302
    char *location;

    // POST数据
    const char *rawData;

    const char *kvData;

    cHTTPFormPostData *dataData;
    size_t dataDataSize;

    const char *raw2Data;
    const char *contentType;

    // 自定义头部
    const char *diy;
};

static int cHTTPFormNet_Connect(cHTTPForm *form)
{
    cHTTPFormNet *net = NULL;

    net = (cHTTPFormNet *)malloc(sizeof(cHTTPFormNet));
    if (!net) return CHTTP_MEM_ERR;

    // +-----------------------------
    // | 打通HTTP代理隧道 
    // +-----------------------------
    if (form->proxy != NULL) {
        char proxyAuth[2048] = {0};

        if (form->proxy->auth) {
            sprintf(proxyAuth, 
                "CONNECT %s:%d HTTP/1.1\r\n"
                "Proxy-Authorization: Basic %s\r\n"
                "\r\n", form->doname, form->port, form->proxy->auth);
        } else {
            sprintf(proxyAuth, 
                "CONNECT %s:%d HTTP/1.1\r\n"
                "\r\n", form->doname, form->port);
        }

        net->s = cSOCK_Connect(form->proxy->ip, form->proxy->port, form->timeout);
        if (net->s == 0) {
            free(net);
            return CHTTP_TIMEOUT;
        }

        if (cSOCK_Send(net->s, proxyAuth, strlen(proxyAuth)) != (int)strlen(proxyAuth)) {
            cSOCK_CloseNow(net->s);
            free(net);
            return CHTTP_SEND_ERR;
        }
        
        memset(proxyAuth, 0, sizeof(proxyAuth));
        if (cSOCK_ReadLine(net->s, proxyAuth, sizeof(proxyAuth)) == 0) {
            cSOCK_CloseNow(net->s);
            free(net);
            return CHTTP_RECV_ERR;
        }

        if (strstr(proxyAuth, "200") == NULL) {
            cSOCK_CloseNow(net->s);
            free(net);
            return CHTTP_PROXY_ERR;
        }

        memset(proxyAuth, 0, sizeof(proxyAuth));
        while (cSOCK_ReadLine(net->s, proxyAuth, sizeof(proxyAuth)) == 1 && strcmp(proxyAuth, "\r\n") != 0) {
            memset(proxyAuth, 0, sizeof(proxyAuth));
        }
    }

    // +-----------------------------
    // | 正常连接 
    // +-----------------------------
    else {
        if (is_ipv4(form->doname)) {
            net->s = cSOCK_Connect(form->doname, form->port, form->timeout);
        } else {
            char ip[25] = {0};
            if (!cSOCK_GetHostByName(form->doname, ip, sizeof(ip) - 1)) { 
                free(net); return CHTTP_DNS_ERR;
            }
            net->s = cSOCK_Connect(ip, form->port, form->timeout);
        }
    }

    if (net->s == 0) {
        free(net);
        return CHTTP_TIMEOUT;
    }

    // +-----------------------------
    // | OpenSSL 
    // +-----------------------------
#ifdef USE_OPENSSL
    if (strcmp(form->protocal, "https") == 0) {
        net->ss = cSOCK_SSL_Connect(net->s);
        if (net->ss == NULL) {
            cSOCK_CloseNow(net->s);
            free(net);
            return CHTTP_HTTPS_INIT;
        }
    }
#endif

    form->net = net;
    return CHTTP_OK;
}

int cHTTPFormNet_Send(cHTTPForm *form, const char *data, size_t size)
{
    if (!form || !form->net || !data || size == 0) return 0;

    if (strcmp(form->protocal, "http") == 0)
        return cSOCK_Send(form->net->s, data, size);

#ifdef USE_OPENSSL
    else if (strcmp(form->protocal, "https") == 0)
        return cSOCK_SSL_Send(form->net->ss, data, size);
#endif

    return 0;
}

int cHTTPFormNet_Recv(cHTTPForm *form, char *data, size_t size)
{
    if (!form || !form->net || !data || size == 0) return 0;

    if (strcmp(form->protocal, "http") == 0)
        return cSOCK_Recv(form->net->s, data, size);

#ifdef USE_OPENSSL
    else if (strcmp(form->protocal, "https") == 0)
        return cSOCK_SSL_Recv(form->net->ss, data, size);
#endif

    return 0;
}

int cHTTPFormNet_ReadLine(cHTTPForm *form, char *data, size_t size)
{
    if (!form || !form->net || !data || size == 0) return 0;

    if (strcmp(form->protocal, "http") == 0) 
        return cSOCK_ReadLine(form->net->s, data, size);

#ifdef USE_OPENSSL
    else if (strcmp(form->protocal, "https") == 0)
        return cSOCK_SSL_ReadLine(form->net->ss, data, size);
#endif

    return 0;
}

void cHTTPFormNet_Close(cHTTPForm *form)
{
    if (!form || !form->net) return;
    
    if (strcmp(form->protocal, "http") == 0) {
        cSOCK_Close(form->net->s);
    }

#ifdef USE_OPENSSL
    else if (strcmp(form->protocal, "https") == 0) {
        cSOCK_SSL_Close(&form->net->ss);
        cSOCK_Close(form->net->s);
    }
#endif

    free(form->net);
    form->net = NULL;
}

void cHTTPFormNet_CloseNow(cHTTPForm *form)
{
    if (!form || !form->net) return;
    
    if (strcmp(form->protocal, "http") == 0) {
        cSOCK_CloseNow(form->net->s);
    }

#ifdef USE_OPENSSL
    else if (strcmp(form->protocal, "https") == 0) {
        cSOCK_SSL_Close(&form->net->ss);
        cSOCK_CloseNow(form->net->s);
    }
#endif

    free(form->net);
    form->net = NULL;
}

// +-------------------------------------------------------
// | http/https 
// +-------------------------------------------------------

/** 创建chttp表单 **/
cHTTPForm *cHTTPForm_Create(const char *url)
{
    cHTTPForm *form = NULL;

    form = (cHTTPForm *)malloc(sizeof(cHTTPForm));
    if (form == NULL) return NULL;
    memset(form, 0, sizeof(cHTTPForm));

    form->url = url;
    form->method = C_HTTP_GET;
    form->timeout = 10;
    form->net = NULL;
    return form;
}

void cHTTPForm_Free(cHTTPForm **form)
{
    if (form == NULL || *form == NULL) return;
    
    cHTTPFormNet_Close(*form);
    if ((*form)->doname) free((*form)->doname);
    if ((*form)->path) free((*form)->path);
    free(*form);
    *form = NULL;
}

/** 设置GET/POST **/
void cHTTPForm_SetMethod(cHTTPForm *form, cHTTPFormMethod method)
{
    if (form) form->method = method;
}

/** 设置超时时间 **/
void cHTTPForm_SetTimeOut(cHTTPForm *form, size_t timeout)
{
    if (form) form->timeout = timeout;
}

/** 设置自定义头部 **/
void cHTTPForm_SetDiy(cHTTPForm *form, const char *diy)
{
    if (form) form->diy = diy;
}

/** 设置HTTP代理服务器 **/
void cHTTPForm_SetHTTPProxy(cHTTPForm *form, const char *ip, int port, const char *user, const char *pwd)
{
    cHTTPFormNetHTTPProxy *proxy = NULL;

    if (!form || !ip || strlen(ip) == 0 || port <= 0)
        return;

    proxy = (cHTTPFormNetHTTPProxy *)malloc(sizeof(cHTTPFormNetHTTPProxy));
    if (proxy == NULL) return;
    memset(proxy->auth, 0, sizeof(cHTTPFormNetHTTPProxy));

    proxy->ip = ip;
    proxy->port = port;

    if (user && strlen(user) != 0 && pwd && strlen(pwd) != 0) {
        char *buf = (char *)malloc(strlen(user) + strlen(pwd) + 2 + 5);
        if (!buf) return;
        sprintf(buf, "%s:%s", user, pwd);
        proxy->auth = _http_base64_encode(buf);
        free(buf);
    }
}

/** 设置回调函数 **/
void cHTTPForm_SetCallBack(cHTTPForm *form, void *p, void (*cb)(void *p, unsigned char *data, size_t size))
{
    if (form) {
        form->p = p;
        form->cb = cb;
    }
}

/** 设置HTTP头回调函数 **/
void cHTTPForm_SetHeadCallBack(cHTTPForm *form, void *p, void (*cb)(void *p, const char *temp))
{
    if (form) {
        form->p2 = p;
        form->cb2 = cb;
    }
}

// +-------------------------------------------------------
// | POST数据4种提交方式 
// | 以下四种数据设置方式只能存在一种, 后设置的将覆盖 
// | 前设置的 
// +-------------------------------------------------------

/** 1. Content-Type: text/plain 原始数据 **/
void cHTTPForm_SetPostData_RAW(cHTTPForm *form, const char *data)
{
    if (!form || !data) return;

    form->method = C_HTTP_POST;
    form->rawData = data;
    form->kvData = NULL;
    form->dataData = NULL;
    form->dataDataSize = 0;
    form->raw2Data = NULL;
    form->contentType = NULL;
}

/** 2. Content-Type: application/x-www-form-urlencoded 非ASCALL进行编码 **/
void cHTTPForm_SetPostData_KV(cHTTPForm *form, const char *data)
{
    if (!form || !data) return;

    form->method = C_HTTP_POST;
    form->rawData = NULL;
    form->kvData = data;
    form->dataData = NULL;
    form->dataDataSize = 0;
    form->raw2Data = NULL;
    form->contentType = NULL;
}

/** 3. Content-Type: multipart/form-data 原始数据 + 文件 **/
void cHTTPForm_SetPostData_Data(cHTTPForm *form, cHTTPFormPostData *data, size_t size)
{
    if (!form || !data || size == 0) return;

    form->method = C_HTTP_POST;
    form->rawData = NULL;
    form->kvData = NULL;
    form->dataData = data;
    form->dataDataSize = size;
    form->raw2Data = NULL;
    form->contentType = NULL;
}

/** 4. Content-Type: xxx 原始数据 **/
void cHTTPForm_SetPostData_Raw2(cHTTPForm *form, const char *contentType, const char *data)
{
    if (!form || !data) return;
    
    form->method = C_HTTP_POST;
    form->rawData = NULL;
    form->kvData = NULL;
    form->dataData = NULL;
    form->dataDataSize = 0;
    form->raw2Data = data;
    form->contentType = contentType;
}

int cHTTPForm_Submit(cHTTPForm *form)
{
    int recode = 0;
    
    // 解析URL使用
    const char *p = NULL, *p_b = NULL;

    // 发送时使用
    char *temp = NULL, *tempb = NULL;
    int temp_size = 0;

    // POST数据使用
    const char *contentType = NULL;
    int contentLen = 0;
    int dataIndex = 0, dataSendSize = 0;
    char Content_Disposition[1024] = {0};
    unsigned char postDataTemp[1024] = {0};

    // 接收时使用
    int Content_Length = 0;
    int Byte_Length = 0;
    int Transfer_Encoding_Chunked = 0;
    int Connection_Close = 0;
    char *location = NULL;
    unsigned char recvdata[3072] = {0};

    if (form == NULL || form->url == NULL) return CHTTP_MEM_ERR;

    // +--------------------------------------
    // | 如果存在上次的连接信息,无需再次连接
    // | 前提服务器支持Connection: keep-alive 
    // +--------------------------------------
    if (form->net == NULL) {
        p = form->location ? form->location : form->url;

        // +--------------------------------------
        // | 解析URL 
        // +--------------------------------------
        if (strncmp(p, "http://", 7) == 0) {
            form->protocal = "http";
            p += 7;
        }
    #ifdef USE_OPENSSL
        else if (strncmp(p, "https://", 8) == 0) {
            form->protocal = "https";
            p += 8;
        }
    #endif
        else return CHTTP_URL_ERR;

        // +--------------------------------------
        // | 解析域名/端口/路径 
        // +--------------------------------------
        p_b = p;
        while (1) {
            if (*p == '\0' || *p == '/' || *p == ':') {
                form->doname = substr(p_b, 0, p - p_b);

                if (*p == ':') {
                    form->port = atoi(++p);
                    while (*p != '\0' && *p != '/') p++;
                } else {
                    if (strcmp(form->protocal, "http") == 0) form->port = 80;
                    else form->port = 443;
                }

                if (*p == '\0') {
                    form->path = strdup("/");
                } else {
                    form->path = strdup(p);
                }

                break;
            }
            p++;
        }

        if (!form->doname || strlen(form->doname) == 0 || !form->path) {
            if (form->doname) free(form->doname);
            if (form->path) free(form->path);
            form->protocal = NULL;
            form->doname = NULL;
            form->path = NULL;
            form->port = 0;
            return CHTTP_URL_ERR;
        }

        // +--------------------------------------
        // | 连接服务器 
        // +--------------------------------------
        recode = cHTTPFormNet_Connect(form);
        if (recode != CHTTP_OK) {
            if (form->doname) free(form->doname);
            if (form->path) free(form->path);
            form->protocal = NULL;
            form->doname = NULL;
            form->path = NULL;
            form->port = 0;
            return recode;
        }
        cSOCK_SetTimeout(form->net->s, form->timeout);
    }

    // +--------------------------------------
    // | 发送HTTP请求 
    // +--------------------------------------
   
    temp_size = strlen(form->path) + 1024;
    temp = (char *)malloc(temp_size);   // 访问路径 + 1024 差不多够用了
    if (temp == NULL) {
        cHTTPFormNet_CloseNow(form);
        return CHTTP_MEM_ERR;
    }
    memset(temp, 0, temp_size);

    if (form->method == C_HTTP_GET) {
        if (form->diy) {
            sprintf(temp, 
                "GET %s HTTP/1.1\r\n"
                "Host: %s\r\n"
                "Connection: keep-alive\r\n"
                "%s"
                CHTTP_Use_Agent
                "\r\n", form->path, form->doname, form->diy);
        } else {
            sprintf(temp, 
                "GET %s HTTP/1.1\r\n"
                "Host: %s\r\n"
                "Connection: keep-alive\r\n"
                CHTTP_Use_Agent
                "\r\n", form->path, form->doname);
        }
    } else {
        if (form->rawData) {
            contentType = "text/plain";
            contentLen = strlen(form->rawData);
        } else if (form->kvData) {
            contentType = "application/x-www-form-urlencoded";
            contentLen = strlen(form->kvData);
        } else if (form->dataData) {
            contentType = "multipart/form-data;boundary="CHTTP_Boundary;
            contentLen = 0;

            // +----------------------------------------------------------------
            // | multipart/form-data，将表单的数据处理为一条消息，以标签为单元，
            // | 用分隔符分开。既可以上传键值对，也可以上传文件
            // +----------------------------------------------------------------
            for (dataIndex = 0; dataIndex < (int)form->dataDataSize; dataIndex++) {
                if (form->dataData[dataIndex].type == 0) {        //文本数据
                    contentLen += (2 + strlen(CHTTP_Boundary) + 2);
                    contentLen += (strlen("Content-Disposition: form-data; name=\"\"") + strlen(form->dataData[dataIndex].name) + 2);
                    contentLen += 2;
                    contentLen += strlen(form->dataData[dataIndex].data);
                    contentLen += 2;
                }

                else if (form->dataData[dataIndex].type == 1) {   //文件
                    if (form->dataData[dataIndex].fileHandle) {
                        contentLen += (2 + strlen(CHTTP_Boundary) + 2);
                        contentLen += (strlen("Content-Disposition: form-data; name=\"\"; filename=\"\"") + 
                            strlen(form->dataData[dataIndex].name) + 
                            strlen(form->dataData[dataIndex].fileName) + 
                            2);
                        contentLen += 2;
                        contentLen += readfilesize(form->dataData[dataIndex].fileHandle);
                        contentLen += 2;
                    }
                }
            }
            if (contentLen > 0)
                contentLen += (2 + strlen(CHTTP_Boundary) + 2 + 2);

        } else if (form->raw2Data) { 
            contentType = form->contentType;
            contentLen = strlen(form->raw2Data);
        } else {
            contentType = "application/octet-stream";
            contentLen = 0;
        }

        if (form->diy) {
            sprintf(temp, 
                "POST %s HTTP/1.1\r\n"
                "Host: %s\r\n"
                "Connection: keep-alive\r\n"
                CHTTP_Use_Agent
                "Content-Type: %s\r\n"
                "Content-Length: %d\r\n"
                "%s"
                "\r\n", form->path, form->doname, contentType, contentLen, form->diy);
        } else {
            sprintf(temp, 
                "POST %s HTTP/1.1\r\n"
                "Host: %s\r\n"
                "Connection: keep-alive\r\n"
                CHTTP_Use_Agent
                "Content-Type: %s\r\n"
                "Content-Length: %d\r\n"
                "\r\n", form->path, form->doname, contentType, contentLen);
        }
    }

    recode = cHTTPFormNet_Send(form, temp, strlen(temp));
    if (recode != (int)strlen(temp)) {
        free(temp);
        cHTTPFormNet_CloseNow(form);
        return CHTTP_SEND_ERR;
    }

    // 发送 POST 数据
    if (form->method == C_HTTP_POST) {
        if (form->rawData) {
            recode = cHTTPFormNet_Send(form, form->rawData, strlen(form->rawData));
        } else if (form->kvData) {
            recode = cHTTPFormNet_Send(form, form->kvData, strlen(form->kvData));
        } else if (form->dataData && contentLen > 0) { 
            recode = 0;

            // +----------------------------------------------------------------
            // | multipart/form-data，将表单的数据处理为一条消息，以标签为单元，
            // | 用分隔符分开。既可以上传键值对，也可以上传文件
            // +----------------------------------------------------------------
            for (dataIndex = 0; dataIndex < (int)form->dataDataSize; dataIndex++) {
                if (form->dataData[dataIndex].type == 0) {        //文本数据
                    memset(Content_Disposition, 0, sizeof(Content_Disposition));
                    sprintf(Content_Disposition, "--%s\r\n", CHTTP_Boundary);
                    dataSendSize = cHTTPFormNet_Send(form, Content_Disposition, strlen(Content_Disposition));
                    if (dataSendSize != (int)strlen(Content_Disposition)) { free(temp); cHTTPFormNet_CloseNow(form); return CHTTP_SEND_ERR; }
                    recode += strlen(Content_Disposition);

                    memset(Content_Disposition, 0, sizeof(Content_Disposition));
                    sprintf(Content_Disposition, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", form->dataData[dataIndex].name);
                    dataSendSize = cHTTPFormNet_Send(form, Content_Disposition, strlen(Content_Disposition));
                    if (dataSendSize != (int)strlen(Content_Disposition)) { free(temp); cHTTPFormNet_CloseNow(form); return CHTTP_SEND_ERR; }
                    recode += strlen(Content_Disposition);

                    dataSendSize = cHTTPFormNet_Send(form, form->dataData[dataIndex].data, strlen(form->dataData[dataIndex].data));
                    if (dataSendSize != (int)strlen(form->dataData[dataIndex].data)) { free(temp); cHTTPFormNet_CloseNow(form); return CHTTP_SEND_ERR; }
                    recode += strlen(form->dataData[dataIndex].data);

                    dataSendSize = cHTTPFormNet_Send(form, "\r\n", 2);
                    if (dataSendSize != 2) { free(temp); cHTTPFormNet_CloseNow(form); return CHTTP_SEND_ERR; }
                    recode += 2;
                }

                else if (form->dataData[dataIndex].type == 1) {   //文件
                    if (form->dataData[dataIndex].fileHandle) {
                        memset(Content_Disposition, 0, sizeof(Content_Disposition));
                        sprintf(Content_Disposition, "--%s\r\n", CHTTP_Boundary);
                        dataSendSize = cHTTPFormNet_Send(form, Content_Disposition, strlen(Content_Disposition));
                        if (dataSendSize != (int)strlen(Content_Disposition)) { free(temp); cHTTPFormNet_CloseNow(form); return CHTTP_SEND_ERR; }
                        recode += strlen(Content_Disposition);

                        memset(Content_Disposition, 0, sizeof(Content_Disposition));
                        sprintf(Content_Disposition, "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n\r\n", 
                            form->dataData[dataIndex].name, form->dataData[dataIndex].fileName);
                        dataSendSize = cHTTPFormNet_Send(form, Content_Disposition, strlen(Content_Disposition));
                        if (dataSendSize != (int)strlen(Content_Disposition)) { free(temp); cHTTPFormNet_CloseNow(form); return CHTTP_SEND_ERR; }
                        recode += strlen(Content_Disposition);

                        if (readfilesize(form->dataData[dataIndex].fileHandle) > 0) {
                            dataSendSize = fread(postDataTemp, 1, sizeof(postDataTemp), form->dataData[dataIndex].fileHandle);
                            while (dataSendSize > 0) {
                                if (cHTTPFormNet_Send(form, postDataTemp, dataSendSize) != dataSendSize) {
                                    free(temp); cHTTPFormNet_CloseNow(form); return CHTTP_SEND_ERR;
                                }
                                recode += dataSendSize;
                                dataSendSize = fread(postDataTemp, 1, sizeof(postDataTemp), form->dataData[dataIndex].fileHandle);
                            }
                        }

                        dataSendSize = cHTTPFormNet_Send(form, "\r\n", 2);
                        if (dataSendSize != 2) { free(temp); cHTTPFormNet_CloseNow(form); return CHTTP_SEND_ERR; }
                        recode += 2;
                    }
                }
            }

            memset(Content_Disposition, 0, sizeof(Content_Disposition));
            sprintf(Content_Disposition, "--%s--\r\n", CHTTP_Boundary);
            dataSendSize = cHTTPFormNet_Send(form, Content_Disposition, strlen(Content_Disposition));
            if (dataSendSize != (int)strlen(Content_Disposition)) { free(temp); cHTTPFormNet_CloseNow(form); return CHTTP_SEND_ERR; }
            recode += strlen(Content_Disposition);

        } else if (form->raw2Data) { 
            recode = cHTTPFormNet_Send(form, form->raw2Data, strlen(form->raw2Data));
        }
        
        if (recode != contentLen) {
            free(temp);
            cHTTPFormNet_CloseNow(form);
            return CHTTP_SEND_ERR;
        }
    }

    // +--------------------------------------
    // | 解析HTTP返回头 
    // +--------------------------------------
    memset(temp, 0, temp_size);
    if (cHTTPFormNet_ReadLine(form, temp, temp_size) == 0) {
        free(temp);
        cHTTPFormNet_CloseNow(form);
        return CHTTP_RECV_ERR;
    }

    if (form->cb2) form->cb2(form->p2, temp);

    if (strstr(temp, "200") == NULL && strstr(temp, "301") == NULL && strstr(temp, "302") == NULL) {
        free(temp);
        cHTTPFormNet_CloseNow(form);
        return CHTTP_NOT_200;
    }

    while (1) { 
        memset(temp, 0, temp_size);
        recode = cHTTPFormNet_ReadLine(form, temp, temp_size);
        if (recode == 0) {
            free(temp);
            cHTTPFormNet_CloseNow(form);
            return CHTTP_RECV_ERR;
        }

        if (form->cb2) form->cb2(form->p2, temp);

        if (strcmp(temp, "\r\n") == 0) {
            break;
        }

        if (stristr(temp, "Content-Length: ") != NULL) {
            tempb = temp + strlen("Content-Length: ");
            while (*tempb == ' ') tempb++;
            Content_Length = atoi(tempb);
        }

        else if (stristr(temp, "Transfer-Encoding: chunked") != NULL) {
            Transfer_Encoding_Chunked = 1;
        }

        else if (stristr(temp, "Connection: close") != NULL) {
            Connection_Close = 1;
        }

        else if (stristr(temp, "Location:") != NULL) {
            tempb = temp + strlen("Location: ");
            while (*tempb == ' ') tempb++;
            location = strdup(tempb);
        }
    }

    // +--------------------------------------
    // | 301/302跳转 
    // +--------------------------------------
    if (location != NULL) {
        cHTTPFormNet_CloseNow(form);
        form->location = location;
        return cHTTPForm_Submit(form);
    }

    // +--------------------------------------
    // | 获取数据 Transfer-Encoding: chunked
    // +-------------------------------------
    if (Transfer_Encoding_Chunked == 1) {
        memset(temp, 0, temp_size);

        while (1) {
            recode = cHTTPFormNet_ReadLine(form, temp, temp_size);
            if (recode == 0) {
                free(temp);
                cHTTPFormNet_CloseNow(form);
                return CHTTP_RECV_ERR;
            }
            Content_Length = 0;
            sscanf(temp, "%x", &Content_Length);
            if (Content_Length == 0) break;

            while (Content_Length > 0) {
                Byte_Length = Content_Length > sizeof(recvdata) ? sizeof(recvdata) : Content_Length;
                memset(recvdata, 0, sizeof(recvdata));
                recode = cHTTPFormNet_Recv(form, recvdata, Byte_Length);
                if (recode != Byte_Length) {
                    free(temp);
                    cHTTPFormNet_CloseNow(form);
                    return CHTTP_RECV_ERR;
                }
                if (form->cb) form->cb(form->p, recvdata, Byte_Length);
                Content_Length -= Byte_Length;
            }

            memset(temp, 0, temp_size);
            cHTTPFormNet_ReadLine(form, temp, temp_size);
        }

        Content_Length = 0;
    }
    
    // +--------------------------------------
    // | 获取数据 Content-Length: xxx
    // +-------------------------------------
    else if (Content_Length > 0) {
        while (Content_Length > 0) {
            Byte_Length = Content_Length > sizeof(recvdata) ? sizeof(recvdata) : Content_Length;
            memset(recvdata, 0, sizeof(recvdata));
            recode = cHTTPFormNet_Recv(form, recvdata, Byte_Length);
            if (recode != Byte_Length) {
                free(temp);
                cHTTPFormNet_CloseNow(form);
                return CHTTP_RECV_ERR;
            }
            if (form->cb) form->cb(form->p, recvdata, Byte_Length);
            Content_Length -= Byte_Length;
        }
    }

    // +--------------------------------------------------------------------
    // | 没有Content-Length/Transfer-Encoding: chunked 
    // +--------------------------------------------------------------------
    else {
        recode = cHTTPFormNet_Recv(form, recvdata, sizeof(recvdata));
        while (recode > 0) {
            if (form->cb) form->cb(form->p, recvdata, recode);
            recode = cHTTPFormNet_Recv(form, recvdata, sizeof(recvdata));
        }
    }

    free(temp);
    temp = NULL;
    
    // +--------------------------------------
    // | 判断连接是否需要关闭 
    // +--------------------------------------
    if (Connection_Close == 1) {
        cHTTPFormNet_Close(form);
    }

    // 清除location内存
    if (form->location) {
        free(form->location);
        form->location = NULL;
    }

    return CHTTP_OK;
}

// +-------------------------------------------------------
// | function 
// +-------------------------------------------------------

typedef struct cHTTP_Str {
    unsigned char *data;
    size_t size;
    struct cHTTP_Str *next;
} cHTTP_Str;

static void cHTTP_Add(cHTTP_Str **str, unsigned char *data, size_t size)
{
    cHTTP_Str *b = NULL, *bf = NULL;
    
    b = (cHTTP_Str *)malloc(sizeof(cHTTP_Str));
    if (b == NULL) return;
    b->data = (unsigned char *)malloc(size);
    if (b->data == NULL) {
        free(b);
        return;
    }
    memcpy(b->data, data, size);
    b->size = size;
    b->next = NULL;
    
    if (*str == NULL) {
        *str = b;
    } else {
        bf = *str;
        while (bf->next) bf = bf->next;
        bf->next = b;
    }
}

static char *cHTTP_ToChar(cHTTP_Str *str)
{
    cHTTP_Str *b = str;
    int size = 1, i = 0;
    char *data = NULL;

    while (b) {
        size += b->size;
        b = b->next;
    }

    data = (char *)malloc(size);
    if (!data) return NULL;
    memset(data, 0, size);

    i = 0;
    b = str;
    while (b) {
        memcpy(data + i, b->data, b->size);

        i += b->size;
        b = b->next;
    }
    
    return data;
}

static void cHTTP_Free(cHTTP_Str **str)
{
    cHTTP_Str *b = NULL, *bf = NULL;

    if (!str || *str == NULL) return;

    b = (*str);
    while (b) {
        bf = b;
        b = b->next;
        free(bf->data);
        free(bf);
    }

    *str = NULL;
}

static void cHTTP_Get_Proc(void *p, unsigned char *data, size_t size)
{
    cHTTP_Add((cHTTP_Str **)p, data, size);
}

/** 获得网页Text **/
char *cHTTP_Get(const char *url)
{
    return cHTTP_Get_Proxy(url, NULL);
}

/** 获得网页Text (使用HTTP代理) **/
char *cHTTP_Get_Proxy(const char *url, const cHTTPFormNetHTTPProxy *proxy)
{
    cHTTPForm *form = NULL;
    cHTTP_Str *str = NULL;
    int recode = 0;
    char *data = NULL;
    
    if (!url || strlen(url) == 0) return NULL;
    
    form = cHTTPForm_Create(url);
    if (proxy) {
        form->proxy = (cHTTPFormNetHTTPProxy *)malloc(sizeof(cHTTPFormNetHTTPProxy));
        if (form->proxy) {
            form->proxy->ip = proxy->ip;
            form->proxy->port = proxy->port;
            form->proxy->auth = proxy->auth ? strdup(proxy->auth) : NULL;
        }
    }
    cHTTPForm_SetCallBack(form, (void *)&str, cHTTP_Get_Proc);
    recode = cHTTPForm_Submit(form);
    cHTTPForm_Free(&form);
    
    if (recode == CHTTP_OK) {
        if (str == NULL) data = strdup("");
        else data = cHTTP_ToChar(str);
    }
    
    cHTTP_Free(&str);
    return data;
}

static void cHTTP_Down_Proc(void *p, unsigned char *data, size_t size)
{
    fwrite(data, 1, size, p);
}

/** 下载网页 **/
int cHTTP_Down(const char *url, const char *file)
{
    return cHTTP_Down_Proxy(url, file, NULL);
}

/** 下载网页 (使用HTTP代理) **/
int cHTTP_Down_Proxy(const char *url, const char *file, const cHTTPFormNetHTTPProxy *proxy)
{
    cHTTPForm *form = NULL;
    cHTTP_Str *str = NULL;
    int recode = 0;
    FILE *fp = NULL;
    
    if (!url || strlen(url) == 0 || !file || strlen(file) == 0)
        return CHTTP_MEM_ERR;
    
    if ((fp = fopen(file, "wb+")) == NULL) {
        return CHTTP_FILE_ERR;
    }
    
    form = cHTTPForm_Create(url);
    if (proxy) {
        form->proxy = (cHTTPFormNetHTTPProxy *)malloc(sizeof(cHTTPFormNetHTTPProxy));
        if (form->proxy) {
            form->proxy->ip = proxy->ip;
            form->proxy->port = proxy->port;
            form->proxy->auth = proxy->auth ? strdup(proxy->auth) : NULL;
        }
    }
    cHTTPForm_SetCallBack(form, fp, cHTTP_Down_Proc);
    recode = cHTTPForm_Submit(form);
    cHTTPForm_Free(&form);
    fclose(fp);
    
    if (recode != CHTTP_OK) {
        remove(file);
    }
    
    return recode;
}
